package com.kool.kmqtt.server.parser;

import com.kool.kmqtt.server.exception.ErrorCode;
import com.kool.kmqtt.server.exception.ProtocolException;
import com.kool.kmqtt.server.packet.ConnectPayload;
import com.kool.kmqtt.server.packet.ConnectVariableHeader;
import com.kool.kmqtt.server.packet.FixedHeader;
import io.netty.buffer.ByteBuf;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;

/**
 * CONNECT报文解析器
 */
@Slf4j
public class ConnectPacketParser extends PacketParser {
    public ConnectPacketParser(FixedHeader fixedHeader) {
        super(fixedHeader);
    }

    @Override
    public void parseVariableHeader(ByteBuf in) {
        ConnectVariableHeader variableHeader = new ConnectVariableHeader();
        byte protocolNameLengthMSB = in.readByte();
        byte protocolNameLengthLSB = in.readByte();
        int protocolNameLength = (Byte.toUnsignedInt(protocolNameLengthMSB) << 8) + Byte.toUnsignedInt(protocolNameLengthLSB);
        //协议名
        byte[] protocolNameBytes = new byte[protocolNameLength];
        for (int i = 0; i < protocolNameLength; i++) {
            protocolNameBytes[i] = in.readByte();
        }
        String protocolName = new String(protocolNameBytes, StandardCharsets.UTF_8);
        variableHeader.setProtocolName(protocolName);
        //协议等级
        int protocolLevel = in.readByte();
        variableHeader.setProtocolLevel(protocolLevel);
        //连接标志
        int connectFlags = in.readByte();
        //连接标志-保留位
        int reserved = connectFlags & 0x01;
        variableHeader.setReserved(reserved);
        //连接标志-清理会话
        //0-false,1-true
        boolean cleanSession = 0x02 == (connectFlags & 0x02);
        variableHeader.setCleanSession(cleanSession);
        //遗嘱标志
        //0-false,1-true
        boolean willFlag = 0x04 == (connectFlags & 0x04);
        variableHeader.setWillFlag(willFlag);
        //遗嘱QoS
        int willQoS = connectFlags >> 3 & 0x03;
        variableHeader.setWillQoS(willQoS);
        //遗嘱保留
        boolean willRetain = 0x20 == (connectFlags & 0x20);
        variableHeader.setWillRetain(willRetain);
        //密码标志
        boolean passwordFlag = 0x40 == (connectFlags & 0x40);
        variableHeader.setPasswordFlag(passwordFlag);
        //用户名标志
        boolean userNameFlag = 0x80 == (connectFlags & 0x80);
        variableHeader.setUserNameFlag(userNameFlag);
        //保持连接,以秒为单位
        byte keepAliveMSB = in.readByte();
        byte keepAliveLSB = in.readByte();
        int keepAlive = (Byte.toUnsignedInt(keepAliveMSB) << 8) + Byte.toUnsignedInt(keepAliveLSB);
        variableHeader.setKeepAlive(keepAlive);

        packet.setVariableHeader(variableHeader);
        packet.setVariableHeaderLength(10);
    }

    @Override
    public void parsePayload(ByteBuf in) {
        ConnectVariableHeader variableHeader = (ConnectVariableHeader) packet.getVariableHeader();
        ConnectPayload payload = new ConnectPayload();
        //客户端id
        byte clientIdLengthMSB = in.readByte();
        byte clientIdLengthLSB = in.readByte();
        int clientIdLength = (Byte.toUnsignedInt(clientIdLengthMSB) << 8) + Byte.toUnsignedInt(clientIdLengthLSB);

        ByteArrayOutputStream clientIdArray = new ByteArrayOutputStream();
        for (int i = 0; i < clientIdLength; i++) {
            clientIdArray.write(in.readByte());
        }
        String clientId = new String(clientIdArray.toByteArray(), StandardCharsets.UTF_8);
        payload.setClientId(clientId);

        if (variableHeader.isWillFlag()) {
            //如果遗嘱标志被设置为1，有效载荷的下一个字段是遗嘱主题
            byte willTopicLengthMSB = in.readByte();
            byte willTopicLengthLSB = in.readByte();
            int willTopicLength = (Byte.toUnsignedInt(willTopicLengthMSB) << 8) + Byte.toUnsignedInt(willTopicLengthLSB);

            ByteArrayOutputStream willTopicArray = new ByteArrayOutputStream();
            for (int i = 0; i < willTopicLength; i++) {
                willTopicArray.write(in.readByte());
            }
            String willTopic = null;
            willTopic = new String(willTopicArray.toByteArray(), StandardCharsets.UTF_8);
            payload.setWillTopic(willTopic);
            //遗嘱消息
            byte willMessageLengthMSB = in.readByte();
            byte willMessageLengthLSB = in.readByte();
            int willMessageLength = (Byte.toUnsignedInt(willMessageLengthMSB) << 8) + Byte.toUnsignedInt(willMessageLengthLSB);
            ByteArrayOutputStream willMessageArray = new ByteArrayOutputStream();
            for (int i = 0; i < willMessageLength; i++) {
                willMessageArray.write(in.readByte());
            }
            String willMessage = null;
            willMessage = new String(willMessageArray.toByteArray(), StandardCharsets.UTF_8);
            payload.setWillMessage(willMessage);
        }

        if (variableHeader.isUserNameFlag()) {
            //用户名
            byte userNameLengthMSB = in.readByte();
            byte userNameLengthLSB = in.readByte();
            int userNameLength = (Byte.toUnsignedInt(userNameLengthMSB) << 8) + Byte.toUnsignedInt(userNameLengthLSB);
            ByteArrayOutputStream userNameArray = new ByteArrayOutputStream();
            for (int i = 0; i < userNameLength; i++) {
                userNameArray.write(in.readByte());
            }
            String userName = null;
            userName = new String(userNameArray.toByteArray(), StandardCharsets.UTF_8);
            payload.setUserName(userName);
        }

        if (variableHeader.isPasswordFlag()) {
            //密码
            byte passwordLengthMSB = in.readByte();
            byte passwordLengthLSB = in.readByte();
            int passwordLength = (Byte.toUnsignedInt(passwordLengthMSB) << 8) + Byte.toUnsignedInt(passwordLengthLSB);
            ByteArrayOutputStream passwordArray = new ByteArrayOutputStream();
            for (int i = 0; i < passwordLength; i++) {
                passwordArray.write(in.readByte());
            }
            String password = null;
            password = new String(passwordArray.toByteArray(), StandardCharsets.UTF_8);
            payload.setPassword(password);
        }
        packet.setPayload(payload);
    }
}
